home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 3
/
Amiga Tools 3.iso
/
audio
/
deliay014
/
dev
/
src
/
amadeus.s
next >
Wrap
Text File
|
1994-10-10
|
21KB
|
654 lines
incdir Include40/
include misc/AYPlayer.i
include misc/mine.i
incdir ''
*** Amadeus AY Player for my DT2 Play AY Emulator Interface, (C) 1994 Raxoft
*** This is an example how your own AY Player could look like...
* 1 Initial version
* 2 Added pushtranspose (143) a poptranspose (144) commands
;This is how single Amadeus song looks like.
;Note that each module can contain several songs like this one...
;See aym_ structure for more info
STRUCTURE AMAD_Song,0
UWORD ams_getabs ;where the original channelA located in ZX ram
UBYTE ams_andsix ;strange F.F.s thing. Should be 31 normally.
UBYTE ams_loops ;how many loops before songend
UWORD ams_looplen ;len of one song loop in VBIs
WORD ams_fadeoffset ;precise fade specification
UWORD ams_fadelen ;how long to fade (not supported by DT so far)
;set to zero for neverending song
UBYTE ams_assignA ;0-3 specifies, what amiga channel will be
UBYTE ams_assignB ;assigned to each ay channel (A-C, noise)
UBYTE ams_assignC
UBYTE ams_assignN
;original ZX data follow. First three ptrs to init channels
UWORD ams_channelA ;these are the original ZX data.
UWORD ams_channelB ;ayp_getabs says, where the ams_channelA was
UWORD ams_channelC ;located when it was in ZX RAM.
;and so on...
; The Player itself. See AYPlayer.i for more info about players in general.
dd ;Section AmadeusCode,code commented because of QUICK_HACK
AYPLAYERHEADER AMAD
dc.b 12-1 ;custom ayfreq transpose: up octave - 1 tone
AYbase ds.l 1 ;where ay registers should be "outed"
AYass ds.l 1 ;where ay channel assignment should be copied
AYfreq ds.l 1 ;from where we can take AY frequencies
dc.w 0 ;initplayer
dc.w 0 ;endplayer
dc.w initsound-*
dc.w 0 ;endsound
dc.w interrupt-*
dc.w 0 ;No pattern support (patterns are pretty
dc.w 0 ;limitating and bad idea IMHO)
dc.b 'Amadeus 1.0',0
dc.b '(C) 1987 Frantisek Fuka - Fuxoft',0
dc.b '(C) 1992-1994 Patrik Rak - Raxoft',0
even
;Here is the song preparation entry... PlayAY passes us ptr to our custom
;AMAD_SongStructure in a0, so it is our job to initialize our player pointers
;Also we calculate the songlen we will use later.
;Also, this is where we CAN assign AY channels to amiga channels
;(otherwise standard 0,1,2,3 assignment is made.)
initsound
lea ams_channelA(a0),a1
moveq #0,d0
move.w (a0)+,d0 ;careful with the words >32767 :-)
sub.l d0,a1
move.l a1,getabsset ;for ZX -> Amiga RAM relocation
move.b (a0)+,andsix ;FFs internal
moveq #0,d0
move.b (a0)+,d0 ;loops
mulu (a0)+,d0 ;looplen
add.w (a0)+,d0 ;add/sub offset
lea songlen,a1
move.w d0,(a1)+ ;store songlen
move.w (a0)+,(a1) ;store fadelen
move.l AYass(pc),a1
move.l (a0)+,(a1) ;copy channel assignment
move.l a0,a1 ;point to channelA
bra.b cold ;skip to orig. amad init routine
;This routine is called every 1/50th of second (usually)
;Here you should process your music data and load the AY regs
;also, you can do the song len counting here - return null if song should
;continue. To signal end, return non zero value telling how many VBIs should
;DT fade.
interrupt
bsr.w warm ;call orig. amadeus int. routine
;This is how signal songend routine should ALWAYS look like
;Note that you MAY NOT return nonzero value more then once per song!!!
moveq #0,d0 ;signal continue
lea songlen,a0
tst.w (a0)
beq.b .exit ;already signalled
subq.w #1,(a0)+ ;decrease songlen and point to fadelen
bne.b .exit ;not end yet
move.w (a0),d0 ;if zero, then will never fade. Otherwise fade for x VBIs
.exit rts
***************************************************************************
;Amadeus (Amiga Version for AY-3-8912 Emulator) 1.00 by Raxoft 1992
;Based on Original Amadeus V8 (C) Fuxoft 1987
;I am not going to comment this source too detailed, because no one is going
;to read it and understand Amadeus DATA format anyway... So only roughly...
;This code is rewrite from Z80 code, as you can see from the code design.
;I did only few optimalizations, because I didn't want to create some
;incompatibility by my mistake... So have it in mind and don't blame me...
;Code has been sligthly rearanged by me and Delirium in 1994.
;===========================================================================
;Support routines for easier Z-80 emulation in 68000 code
;GetAbs replaces this code, which is used very often in amadeus (and other
;routines as well):
; LD (HL),E ;imagine that HL is a1 and DE is D7
; INC HL
; LD (HL),D
; INC HL
;and DE is not just some value, it is another Address Pointer to ZX data...
;This allows that there is no need to change the original data at all...
getabs moveq #0,d7 ;Simulates reading word address from ZX
move.b (a1)+,d6 ;memory and recalculates it to the
move.b (a1)+,d7 ;amiga memory. Note that ZX has LSB
lsl.w #8,d7 ;followed MSB. And word can be on
move.b d6,d7 ;odd address as well.
add.l getabsset,d7 ;Returns Address in d7, also increases
rts ;that pointer...
;Because data in ZX memory often use direct addressing, it is necessary to
;recalculate it to your Amiga memory. Let's say you have ZX data, which were
;located on 53000, and you have them on $534a3 in your amiga memory. So you
;have to set this to $534a3-53000, otherwise the getabs won't work properly...
getabsset dc.l 0 ;Base for your ZX memory
;===========================================================================
;This code initiates all important values needed later.
;On ZX it also installed IM2 routine.
;a1 contains pointer to first data stream
cold lea worksp,a6 ;Workspace
bsr.b getabs ;Read three channel pointers
move.l d7,(a6) ;and install them to workspace
bsr.b getabs
move.l d7,wrk2(a6)
bsr.b getabs
move.l d7,wrk3(a6)
move.w #$0108,d0 ;Set note len to 1 and strobe to 8
move.w d0,wrk1+len(a6)
move.w d0,wrk2+len(a6)
move.w d0,wrk3+len(a6)
lea stacks+stacksize-worksp(a6),a1 ;Init Channel stacks
move.l a1,stk1-worksp(a6)
lea stacksize(a1),a1
move.l a1,stk2-worksp(a6)
lea stacksize(a1),a1
move.l a1,stk3-worksp(a6)
clr.w wrk1+flags(a6) ;reset flags and transpositions
clr.w wrk2+flags(a6)
clr.w wrk3+flags(a6)
clr.l wrk1+freqbeg(a6);reset frequency envelopes stars
clr.l wrk2+freqbeg(a6);don't think it is necessary
clr.l wrk3+freqbeg(a6);but recoded it as well :-)
sf barva-worksp(a6);Clear noise color/frequency
rts
;===========================================================================
;This was the original routine which was called by Z80 every interrupt, pushed
;registers, called worm, poped registers, EI, RET. Now, it is the same as WARM
;indeed
;inter equ warm
;===========================================================================
;This routine originally disabled IM2, turned on IM1 and stopped AY sound.
;Now it does only the latter, although you can also call AYoff and it will do
;the same as calling StopAMAD & AYemulate. It just puts 255 to the strobe
;register
;No need for this routine, since the playAY will turn of the AY anyway
;(something not possible on ZX :-)
;stopamad lea worksp(pc),a6
; moveq #-1,d0
; bra.b stopout
;===========================================================================
;Main routine, should be called every 50th of second, followed by the call
;to AYemulate.
;Of course that you first have to install Data for amadeus, THEN call Cold,
;and THEN you can call this.
;It does everything: advances in all channels, generates new notes, new
;envelopes, all effects, everything...
warm lea worksp,a6 ;Workspace pointer to a6 as base
lea (a6),a5 ;Proccess Channel 1
move.l stk1-worksp(a6),a4 ;get stack pointer
moveq #1,d0 ;channel number
bsr.b main ;Oh man, DO IT !!!
move.l a4,stk1-worksp(a6) ;store stack pointer
lea wrk2(a6),a5 ;Channel 2
move.l stk2-worksp(a6),a4
moveq #2,d0
bsr.b main
move.l a4,stk2-worksp(a6)
lea wrk3(a6),a5 ;Channel 3
move.l stk3-worksp(a6),a4
moveq #3,d0
bsr.b main
move.l a4,stk3-worksp(a6)
move.b wrk3+master(a6),d0 ;Get enable bits of all
add.b d0,d0 ;three channels and generate
or.b wrk2+master(a6),d0 ;strobe
add.b d0,d0
or.b wrk1+master(a6),d0
stopout moveq #7,d5 ;"Out" it to the AY backup reg.
bsr.b out ;and fall down to outy
;===========================================================================
;routine which originally OUTed all register stored in memory to the AY
;at once. Now, it just copies them from AY Backup registers (note that
;everything was prepared in memory at first) to the "AY register" (in our
;case, just another part of memory:-)
;Originally it was send there from reg 13 to reg 0, but here is no reason
;to do that so... Although in Z80 it was better solution :-)
outy moveq #14/2-1,d5 ;14 register to copy
lea output-worksp(a6),a1 ;pointer to my ay output buffer
move.l AYbase,a3 ;pointer to AY registers
outy1 move.w (a1)+,(a3)+ ;And feed them all :-)
dbra d5,outy1
rts
;===========================================================================
;Originally it was a little longer routine, which stored one byte to the
;output table. Motorola makes it in one instruction :-)
out move.b d0,output-worksp(a6,d5.w)
rts
;===========================================================================
;Main processing routine for single channel
;a4 stack pointer for this buffer
main move.b d0,hlas-worksp(a6) ;store channel number
subq.b #1,len(a5) ;decrease note length, skip
beq.w nova ;if we need to read new one.
hl11 subq.b #1,envper(a5) ;dec volume envelope delay counter
bne.B hlst ;skip if still same volume
move.l envpos(a5),a1 ;Pointer to Vol Envelope stream
hl1 move.b (a1)+,d0 ;Read next volume/command
cmp.b #128,d0 ;is it Jump?
bne.b neskhl ;Skip if not.
bsr.w getabs ;Get the Jump destination
move.l d7,a1 ;
bra.b hl1 ;And continue there...
neskhl cmp.b #30,d0 ;0-30 used for volume 0-15
bcs.b neskh0 ;followed by volume delay
sub.b #50,d0 ;values above 50 used for
move.b d0,vol(a5) ;volume 0-15, which has automatically
move.b #1,envper(a5) ;env volume delay = 1 (often used ->
bra.b contx1 ;safes data)
neskh0 move.b d0,vol(a5) ;Ranges 0-30 and 50-255 are strange,
move.b (a1)+,envper(a5);but better than try to always add
;128 or like by yourself... And FF
;wrote his musics in asm, of course...
contx1 move.l a1,envpos(a5) ;Store the volume envelope position
hlst move.w freq(a5),d0 ;Skip if freq is 0 -> Generates
beq.b playit ;no tone
btst #2,flags(a5) ;Is it first Edge? If it is, so go and
bne.b playit ;playit
move.l freqpos(a5),d0 ;Pointer to Freq envelope stream
beq.b playit ;!!!Just to be sure, FF is sclerotic !!!
move.l d0,a1 ;On ZX it loads 243 from 0, but here... ?
frqc1 moveq #0,d0 ;Get next freq/command
move.b (a1)+,d0
move.l a1,freqpos(a5) ;Store pointer to stream for next time
cmp.b #128,d0 ;Is it jump?
bne.b nojpfr ;skip if not
bsr.w getabs ;Get jump destination address
move.l d7,a1 ;as new stream
bra.b frqc1 ;and continue there
nojpfr cmp.b #130,d0 ;Is it Tone Slide On command?
bne.b nicfr1 ;Nope
bset #3,flags(a5) ;Set that we will do Tone Slides
bra.b frqc1 ;next freq/command
nicfr1 cmp.b #131,d0 ;Is it Freq Slide On command?
bne.b nicfr2 ;Nope
bclr #3,flags(a5) ;Clear it, so we now do Freq Slides
bra.b frqc1 ;next freq/command
nicfr2 cmp.b #132,d0 ;Is it Tone/Noise Toggler?
bne.b nicfr3 ;Nope
eor.b #9,master(a5) ;Toggle Tone / Noise on flags
bra.b frqc1 ;Next command
;d0 was no command, so it is either Tone or Freq change value (depends)
nicfr3 btst #3,flags(a5) ;Are we in Tone Slide mode?
beq.b nojpf0 ;Skip if in Freq Slide mode.
add.b tone(a5),d0 ;Add/Sub value from currently played
move.b d0,tone(a5) ;note and store it back
;Note that following part can perhaps read values out of table, if not
;used carefully in music data! Also note, that value 0 is not here checked for
;silence - so it sometimes happened, that when one channel was playing only
;noise (like drum), and the transposition unforunatelly set the note to zero,
;value from (freq-2) was loaded to freq. It was OK till it was not zero (it
;was never so on ZX Amadeus), but when it happened (and of course it happened),
;value of 0 was loaded to freq here, thus disabling tone (which was disabled
;by strobe anyway), but also noise (by seting volume to zero), although it
;should play... Took me quite a while, why in several songs the drums
;sometimes sounded quite strange... That's the reason why I put
;dc.w 65535 before freqs table in FXS, just to be NOT ZERO... Now, the
;freq table goes up to 13.75 Hz, so there is a non zero value...
add.b d0,d0 ;Get AY frequency from the table
move.l AYfreq(pc),a3 ;Note that first note is 1,
move.w (a3,d0.w),freq(a5) ;not 0 - 0 is for silence
bra.b playit ;and go to play sound
nojpf0 ext.w d0 ;So it is Freq change value
add.w d0,d0 ;Correction - change AY freq change to
add.w d0,d0 ;Amiga freq change...
add.w d0,d0
add.w d0,freq(a5) ;And store new frequency
;Well, so volume and Frequency is already generated, so it is showtime
playit move.b barva-worksp(a6),d0 ;"Out" Noise freq
and.b andsix-worksp(a6),d0 ;FF sometimes use only range 0-15 ?!?!?
moveq #6,d5 ;(Why is it here in single channel processing?)
bsr.w out ;(Well, don't ask me :-)
bclr #2,flags(a5) ;Set flag First Edge of Note played
moveq #7,d5
add.b hlas-worksp(a6),d5 ;Get Channel number + 7 (8-10)
move.w freq(a5),d0 ;Check frequency - if zero, then
beq.b mlc ;no tone AND no noise !!!
move.b vol(a5),d0 ;Else "Out" channel volume
mlc bsr.w out ;Channel volume OR zero
subq.b #8,d5 ;Get channel number - 1 (0-2)
add.b d5,d5 ;Get Freq reg position (0,2,4)
move.b freq(a5),d0 ;"Out" frequency
bsr.w out
addq.b #1,d5
move.b freq+1(a5),d0
bsr.w out
navrat rts ;return
;===========================================================================
;Just store channel position as we proccess it... Originally much longer
;routine, of course :-), because of z80...
;putpos move.l a1,(a5)
; rts
;changed to inline instruction
;and changed spec subroutines so it is not almost used... :-)
putpos macro
move.l a1,(a5)
endm
;===========================================================================
;This is called whenever new note is going to be played
nova move.l (a5),a1 ;Get channel position
nova2 moveq #0,d0 ;Get next note/command
move.b (a1)+,d0
putpos ;Remember position
btst #7,d0 ;Is it special command?
bne.B spec ;Go there if it is
moveq #0,d5 ;Fake freq = 0 -> will cause silence
;later
tst.b d0 ;Is the note 0 (silence) ?
beq.B notran ;Go further if it is...
add.b trans(a5),d0 ;Add current Transposition to note
move.b d0,tone(a5) ;and store it
bclr #3,flags(a5) ;Turn Freq Slides on (default)
add.b d0,d0 ;Get note frequency
move.l AYfreq,a3
move.w (a3,d0.w),d5
notran move.b (a1)+,len(a5) ;Get number of VBIs for which it will
putpos ;sound (0=256) and store pointer
move.w d5,freq(a5) ;store frequency or 0 for silence
move.l freqbeg(a5),freqpos(a5) ;Re-Set Freq Env stream
bset #2,flags(a5) ;Say This is First Edge of Note
;to prevent early Freq Stream proccessing
;in case we will now jump to hl11 to
;keep everything synchronized
;Next part is little tricky. It handles continuous Volume Envelope streams,
;so eg. Volume can still fade even if we play a lot different notes.
;Bit 0 of flags says, if we want to turn this feature on, bit 1 says if
;it is already on. To turn it off, it is necessary turn both bits off.
;It is quite clever, allows control even in music nesting etc.
btst #1,flags(a5) ;Already continuous Vol Envs?
bne.w hl11 ;Proccess it if so..
btst #0,flags(a5) ;Do we want turn it on?
beq.b nic0 ;Skip if not...
bset #1,flags(a5) ;From now, Vol Envs are continuous...
nic0 move.l envbeg(a5),a3 ;Get begin of Vol Env stream
move.b (a3)+,vol(a5) ;and load first volume and delay
move.b (a3)+,envper(a5);note that cannot use that +50 mode
move.l a3,envpos(a5) ;here. Re-Set Vol Env position.
bra.w playit ;And finally, play the note...
;===========================================================================
;This part proccesses all speciall commands found in the main channel stream,
;like gosubs, for-nexts, etc.
;Note that we trust the data - so there is no Command Code check, and no
;Stack overflow check because of too much Gosubs or For-Nexts
spec lea nova2(pc),a0 ;to save some bytes
add.b d0,d0 ;Execute proper routine...
;Perhaps should be slightly
lea.l jptabl(pc,d0.w),a3 ;more CRASH PROOF :-)
add.w (a3),a3
jmp (a3)
jptabl dc.w jp-*,call-*,for_-*,next-*
dc.w zm6-*,zmch-*,frqsta-*,def-*
dc.w transp-*,ret_-*
dc.w join-*,unjoin-*,usr-*
dc.w add6-*,addtra-*
dc.w pushtra-*,poptra-*
;===========================================================================
jp bsr.w getabs ;Stream Jump to some address
move.l d7,a1 ;load new position.
jmp (a0)
;===========================================================================
call bsr.w getabs ;Stream call. Remember position
move.l a1,-(a4)
move.l d7,a1 ;on the stack and load new one.
jmp (a0)
;===========================================================================
for_ move.b (a1)+,-(a4)
subq.l #1,a4
move.l a1,-(a4) ;For. Remember count and position
jmp (a0) ;on the steck
;===========================================================================
next subq.b #1,5(a4)
beq.b next1
move.l (a4),a1
jmp (a0)
next1 addq.l #6,a4
jmp (a0)
;===========================================================================
zm6 move.b (a1)+,barva-worksp(a6) ;change Frequency of Noise Channel
jmp (a0)
;===========================================================================
zmch move.b (a1)+,master(a5);Select Tone AND/OR Noise On/Off
jmp (a0) ;Note also Toggler in Freq Env Stream, used
;to generate pretty interesting sounds...
;===========================================================================
def bsr.w getabs ;Define Volume Envelope Stream begin
move.l d7,envbeg(a5) ;for following notes
jmp (a0)
;===========================================================================
transp move.b (a1)+,trans(a5) ;Change transposition directly
jmp (a0)
;===========================================================================
ret_ move.l (a4)+,a1 ;Return from subchannel. Get position from stack.
jmp (a0)
;===========================================================================
frqsta bsr.w getabs ;Define Frequency Envelope Stream begin
move.l d7,freqbeg(a5) ;for following notes
jmp (a0)
;===========================================================================
join bset #0,flags(a5) ;Turn on Continuous Volume Envelopes
bra.b unjin
; bclr #1,flags(a5)
; jmp (a0)
;===========================================================================
unjoin bclr #0,flags(a5) ;Turn off Continuous Volume Envelopes
unjin bclr #1,flags(a5)
jmp (a0)
;===========================================================================
usr addq.l #2,a1 ;This function was used for USER calls to any
;Z80 code (eg. for synchronizing picture with
jmp (a0) ;sound as in Indiana Jones 3 by F.F.)
;Not implemented, OF COURSE :-)
;===========================================================================
add6 move.b (a1)+,d0 ;Increase/Decrease Noise Channel Freq
add.b d0,barva-worksp(a6) ;Used for some effects...
jmp (a0)
;===========================================================================
addtra move.b (a1)+,d0 ;Increase/Decrease Transposition level
add.b d0,trans(a5)
jmp (a0)
;===========================================================================
pushtra move.b trans(a5),d0 ;Store Transposition level
move.w d0,-(a4)
jmp (a0)
;===========================================================================
poptra move.w (a4)+,d0 ;Retore Transposition level
move.b d0,trans(a5)
jmp (a0)
;===========================================================================
section AmadeusData,bss
output ds.b 14 ;Output space for AY registers
barva ds.b 1 ;Noise Frequency
hlas ds.b 1 ;Temporary, for channel number
stk1 ds.l 1 ;Stack pointers for all three channels
stk2 ds.l 1
stk3 ds.l 1
stacksize equ 128 ;even bigger then on ZX I think...
worksp ds.b 3*30 ;WorkSpace for three channels, see below
stacks ds.b 3*stacksize ;Space for Channel Stacks, used to store
;Gosub & For-Next informations.
wrk1 equ 0 ;Offset definitions
wrk2 equ 30
wrk3 equ 60
data equ 0 ;l ;Current Main Channel position
len equ 4 ;b ;Delay remaining to the end of note
master equ 5 ;b ;Partial Strobe (0,3), see AY strobe
envpos equ 6 ;l ;Current Volume Envelope Stream Pos.
envper equ 10 ;b ;Delay till pickup of next volume
vol equ 11 ;b ;current volume
freq equ 12 ;w ;current frequency
envbeg equ 14 ;l ;Start of Volume Envelope Stream
freqpos equ 18 ;l ;Current Freq Env Stream position.
flags equ 22 ;b ;Flags - see code for more
trans equ 23 ;b ;Current Transposition
freqbeg equ 24 ;l ;Start of Freq Env Stream
tone equ 28 ;b ;Current note
songlen ds.w 1
fadelen ds.w 1
andsix ds.b 1